import { ECharts } from 'echarts';
import { throttle } from 'lodash-es';

/**
 * Chart resizing utility to make charts responsive
 */
export class ChartResizer {
  private static instance: ChartResizer;
  private charts: Map<string, ECharts> = new Map();
  private resizeHandler: () => void;
  private observer: ResizeObserver | null = null;
  private containers: Map<string, Element> = new Map();
  private pendingCharts: Map<string, { chart: ECharts, container: Element, retries: number }> = new Map();
  private retryInterval: number = 100; // ms
  private maxRetries: number = 10;

  private constructor() {
    // Throttle the resize handler to prevent performance issues
    this.resizeHandler = throttle(() => {
      console.log('[ChartResizer] Resizing all charts, count:', this.charts.size);
      this.charts.forEach((chart, id) => {
        try {
          chart.resize();
          console.log(`[ChartResizer] Chart ${id} resized successfully`);
        } catch (error) {
          console.error(`[ChartResizer] Error resizing chart ${id}:`, error);
        }
      });
    }, 200);

    // Listen for window resize events
    window.addEventListener('resize', this.resizeHandler);

    // Initialize ResizeObserver if available in the browser
    if (typeof ResizeObserver !== 'undefined') {
      this.observer = new ResizeObserver((entries) => {
        console.log('[ChartResizer] Container size changed, entries:', entries.length);
        this.resizeHandler();
      });
    } else {
      console.warn('[ChartResizer] ResizeObserver not supported, falling back to window resize events only');
    }

    console.log('[ChartResizer] ChartResizer instance created');
  }

  /**
   * Get the singleton instance
   */
  public static getInstance(): ChartResizer {
    if (!ChartResizer.instance) {
      ChartResizer.instance = new ChartResizer();
    }
    return ChartResizer.instance;
  }

  /**
   * Register a chart to be automatically resized
   * @param id Unique identifier for the chart
   * @param chart ECharts instance
   * @param container The container element (optional)
   */
  public register(id: string, chart: ECharts, container?: Element): void {
    console.log(`[ChartResizer] Registering chart: ${id}`);
    
    if (!container) {
      console.log(`[ChartResizer] No container provided for chart ${id}, registering chart only`);
      this.charts.set(id, chart);
      return;
    }

    // Check if the container has dimensions
    const { clientWidth, clientHeight } = container as HTMLElement;
    console.log(`[ChartResizer] Container dimensions for ${id}:`, clientWidth, clientHeight);
    
    if (clientWidth <= 0 || clientHeight <= 0) {
      // Container doesn't have dimensions yet, queue for retry
      console.warn(`[ChartResizer] Container for ${id} has no dimensions, queueing for retry`);
      this.pendingCharts.set(id, { chart, container, retries: 0 });
      this.retryInitialization(id);
    } else {
      // Container has dimensions, register normally
      this.charts.set(id, chart);
      
      if (this.observer) {
        console.log(`[ChartResizer] Observing container for chart ${id}`);
        this.containers.set(id, container);
        this.observer.observe(container);
      }
      
      // Force a resize to ensure proper rendering
      try {
        chart.resize();
        console.log(`[ChartResizer] Initial resize for chart ${id} successful`);
      } catch (error) {
        console.error(`[ChartResizer] Error during initial resize for chart ${id}:`, error);
      }
    }
  }

  /**
   * Retry chart initialization for containers without dimensions
   */
  private retryInitialization(id: string): void {
    const pendingChart = this.pendingCharts.get(id);
    if (!pendingChart) return;

    setTimeout(() => {
      const { chart, container, retries } = pendingChart;
      const { clientWidth, clientHeight } = container as HTMLElement;
      console.log(`[ChartResizer] Retry ${retries + 1} for chart ${id}, dimensions:`, clientWidth, clientHeight);

      if (clientWidth > 0 && clientHeight > 0) {
        // Container now has dimensions, register normally
        console.log(`[ChartResizer] Container for ${id} now has dimensions, registering`);
        this.charts.set(id, chart);
        
        if (this.observer) {
          this.containers.set(id, container);
          this.observer.observe(container);
        }
        
        // Force a resize to ensure proper rendering
        try {
          chart.resize();
          console.log(`[ChartResizer] Initial resize for chart ${id} successful after retry`);
        } catch (error) {
          console.error(`[ChartResizer] Error during initial resize for chart ${id} after retry:`, error);
        }
        
        // Remove from pending charts
        this.pendingCharts.delete(id);
      } else if (retries < this.maxRetries) {
        // Still no dimensions, retry
        console.warn(`[ChartResizer] Container for ${id} still has no dimensions, retry ${retries + 1}/${this.maxRetries}`);
        this.pendingCharts.set(id, { chart, container, retries: retries + 1 });
        this.retryInitialization(id);
      } else {
        // Max retries reached, register anyway and hope for the best
        console.warn(`[ChartResizer] Chart container for "${id}" has no dimensions after ${this.maxRetries} retries, registering anyway`);
        this.charts.set(id, chart);
        
        if (this.observer) {
          this.containers.set(id, container);
          this.observer.observe(container);
        }
        
        // Remove from pending charts
        this.pendingCharts.delete(id);
      }
    }, this.retryInterval);
  }

  /**
   * Unregister a chart
   * @param id Chart identifier
   */
  public unregister(id: string): void {
    console.log(`[ChartResizer] Unregistering chart: ${id}`);
    
    // Clear any pending initialization
    this.pendingCharts.delete(id);
    
    if (this.containers.has(id) && this.observer) {
      const container = this.containers.get(id);
      if (container) {
        console.log(`[ChartResizer] Stopping observation for container of chart ${id}`);
        this.observer.unobserve(container);
      }
      this.containers.delete(id);
    }
    
    this.charts.delete(id);
  }

  /**
   * Resize a specific chart
   * @param id Chart identifier
   */
  public resizeChart(id: string): void {
    const chart = this.charts.get(id);
    if (chart) {
      try {
        chart.resize();
        console.log(`[ChartResizer] Manually resized chart ${id}`);
      } catch (error) {
        console.error(`[ChartResizer] Error resizing chart ${id}:`, error);
      }
    } else {
      console.warn(`[ChartResizer] Cannot resize chart ${id}, not found`);
    }
  }

  /**
   * Resize all registered charts
   */
  public resizeAll(): void {
    console.log(`[ChartResizer] Manually resizing all ${this.charts.size} charts`);
    this.resizeHandler();
  }

  /**
   * Clean up resources when no longer needed
   */
  public destroy(): void {
    console.log('[ChartResizer] Destroying ChartResizer instance');
    window.removeEventListener('resize', this.resizeHandler);
    
    if (this.observer) {
      this.containers.forEach((container) => {
        this.observer?.unobserve(container);
      });
      this.containers.clear();
    }
    
    this.charts.clear();
    this.pendingCharts.clear();
  }
} 